
//#define DEBUG_TEST
/*******************************************************************************
*	HEADER FILES
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/gpio.h>

#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
//#include <asm/io.h>

#ifndef DEBUG_TEST
#include <mach/irqs.h>
#include <mach/io.h>
#include <mach/cputype.h>
#include <mach/hardware.h>
//#include "globaldef.h"
#endif

/*******************************************************************************
*	LOCAL DEFINES
*/

#define DEVICE_NAME "gpio_ww"
#define GPIO_MAJOR 232

#ifndef DEBUG_TEST
#define __REG(addr)   (*(volatile unsigned int *)IO_ADDRESS(addr))
#define REG_PINMUX0     __REG(0x01c40000)
#define REG_PINMUX1     __REG(0x01c40004)
#define REG_PINMUX2     __REG(0x01c40008)
#define REG_PINMUX3     __REG(0x01c4000c)
#define REG_PINMUX4     __REG(0x01c40010)
#endif

#define SCK_LOOP 	100
#define BUFSIZE		32

enum GPIO_CTRL_CMD
{
  GPIO_READY_CTRL = 0,
  GPIO_AFTERRUN_CTRL,
  GPIO_RUN_CTRL,
  GPIO_ALARM_CTRL
};
/*******************************************************************************
*	FILE GLOBALS
*/
static int gpio_ready    = 29; //output when ready
static int gpio_run      = 31; //input for run ctrl
static int gpio_afterrun = 32; //output when after run
static int gpio_alarm    = 33; //input when there is some alarm

//static int gpio_delay_us   = 1;
//static int gpio_wake_us   = 100;
//static int gpio_wait_us   = 4000;

static unsigned int gpio_major = GPIO_MAJOR;
static unsigned int gpio_minor = 0;
static struct cdev gpio_dev;
unsigned char recv_buf[BUFSIZE];
unsigned char send_buf[BUFSIZE];
static struct class *gpiodev_class;

/*--------------signal related---------------------*/
const int irq = 34;
struct fasync_struct *fasync_queue;
static int my_fasync(int fd, struct file * filp, int on) 
{
  int retval;
  retval=fasync_helper(fd,filp,on,&fasync_queue);
  return retval;
}
/*--------------signal related end---------------*/
static irqreturn_t irq_handler (int irq, void *dev_id)
{
  if (fasync_queue)
    kill_fasync(&fasync_queue, SIGIO, POLL_IN);

  return IRQ_HANDLED;
}
static int gpio_irq_init(void)
{
  int err;
  err = request_irq(irq,irq_handler,IRQF_DISABLED,DEVICE_NAME,NULL);
  if (err){
    if(err == -EBUSY)
      printk(KERN_ALERT "request_irq %d busy!\n",irq);
    else
      printk(KERN_ALERT "request_irq failed!\n");
    return err;
  }
  return 0;
}

/*delay time unit: us */
void gpioDelay(unsigned int sck)
{
  udelay(sck);
}
int gpio_IO_init(void)
{
  int status = 0;
#ifndef DEBUG_TEST
  /* request the gpios */

  status = gpio_request(gpio_ready, DEVICE_NAME);
  if(status<0)
  {    
    printk("<1>""the gpio %d is busy!\n",gpio_ready);
    return status;
  }
  status = gpio_request(gpio_run, DEVICE_NAME);
  if(status<0)
  {    
    printk("<1>""the gpio %d is busy!\n",gpio_run);
    return status;
  }
  status = gpio_request(gpio_afterrun, DEVICE_NAME);
  if(status<0)
  {    
    printk("<1>""the gpio %d is busy!\n",gpio_afterrun);
    return status;
  }
  status = gpio_request(gpio_alarm, DEVICE_NAME);
  if(status<0)
  {    
    printk("<1>""the gpio %d is busy!\n",gpio_alarm);
    return status;
  }

  /* gpio pins mux set */
  REG_PINMUX4 &= ~(1 << 5);  //GIO29 used as gpio
  REG_PINMUX4 &= ~(1 << 4);
  REG_PINMUX4 &= ~(1 << 9);  //GIO31 used as gpio
  REG_PINMUX4 &= ~(1 << 8);

  /* gpio direction set */
  gpio_direction_output(gpio_ready, 1);
  gpio_direction_output(gpio_afterrun, 1);
  gpio_direction_input(gpio_run);
  gpio_direction_input(gpio_alarm);

  /* gpio output set 1*/
  gpio_set_value(gpio_ready, 1);
  gpio_set_value(gpio_afterrun, 1);

#endif
  return status;
}

/* -------------------------------------------------------------------------*
* Function   : gpioWrBit                                                    *
* Description: This function is used to transfer a bit to gpio bus.         *
* Parameters : None                                                        *
* Return     : None                                                        *
* -------------------------------------------------------------------------*/
u8 gpioWrBit(int vbit)
{
  u8 readbit = 0;
  return readbit;
}

static int gpio_open(struct inode *inode, struct file *filp)
{
  /* Init the board addrss */
  int status;
  printk(KERN_ALERT "Init the gpios for general use.\n");
  status = gpio_IO_init();
  if(status < 0)
    return status;
  status = gpio_irq_init();
  return status;
}
static int gpio_release(struct inode *inode, struct file *file)
{
  printk(KERN_ALERT "close the gpios for general use.\n");
  gpio_free(gpio_ready);
  gpio_free(gpio_run);
  gpio_free(gpio_afterrun);
  gpio_free(gpio_alarm);
  free_irq(irq,NULL);
  return 0;
}
static ssize_t gpio_read(struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
  return 0;
}

static ssize_t gpio_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
  ssize_t		      status = 0;

  return status;
}
static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
  long retval = 0;
  switch(cmd)
  {
  case GPIO_READY_CTRL:
    if(arg>0)
      arg = 1;
    gpio_set_value(gpio_ready, arg);
    break;
  default:
    break;
  }
  return retval;
}

static int gpio_create_dev(void)
{
  struct device *dev;
  int status;
  gpiodev_class = class_create(THIS_MODULE, DEVICE_NAME); //register a class to allow "mdev" in "/dev/"
  if (IS_ERR(gpiodev_class)) {
    return PTR_ERR(gpiodev_class);
  }

  dev = device_create(gpiodev_class,NULL, MKDEV(gpio_major, 0), NULL, DEVICE_NAME);

  status = IS_ERR(dev) ? PTR_ERR(dev) : 0;

  return status;
}
static struct file_operations gpio_fops = {
  .owner = THIS_MODULE,
  .open  = gpio_open,
  .read  = gpio_read,
  .write = gpio_write,
  .unlocked_ioctl = gpio_ioctl,
  .release = gpio_release,
  .fasync = my_fasync,
};

static int __init gpio_init(void)
{
  int res;
  dev_t devon = MKDEV(gpio_major, 0);
  gpio_release(NULL,NULL);
  if(gpio_major)
    res = register_chrdev_region(devon, 1, DEVICE_NAME);
  else
    res = alloc_chrdev_region(&devon, 0, 1, DEVICE_NAME);
  if(res < 0)	{
    printk(KERN_ALERT "ERROR: gpio cannot get device number!\n");
    return res;
  }

  gpio_major = MAJOR(devon);
  gpio_minor = MINOR(devon);

  cdev_init(&gpio_dev, &gpio_fops);
  gpio_dev.owner = THIS_MODULE;
  gpio_dev.ops = &gpio_fops;

  if( cdev_add(&gpio_dev, devon, 1) < 0)	{
    printk(KERN_ALERT "Add the gpio device error\n");
    unregister_chrdev_region(devon, 1);
    return -1;
  }

  //printk(KERN_ALERT "You must create the dev file manually.\n");
  //printk(KERN_ALERT "TODO: mknod /dev/%s c %d %d\n",DEVICE_NAME, gpio_major, gpio_minor);
  if(gpio_create_dev() < 0)
  {
    printk(KERN_ALERT "can not create the gpio device file in /dev/\n");
    unregister_chrdev_region(devon, 1);
    return -1;
  }
  printk(KERN_ALERT "TODO: device file has been made: /dev/%s\n",DEVICE_NAME);

  return 0;
}

static void __exit gpio_exit(void)
{
  printk(KERN_ALERT "The gpio general driver exit!\n");
  device_destroy(gpiodev_class,MKDEV(gpio_major, 0));
  class_destroy(gpiodev_class);
  cdev_del(&gpio_dev);
  unregister_chrdev_region(MKDEV(gpio_major, 0), 1);
}

module_init(gpio_init);
module_exit(gpio_exit);

MODULE_AUTHOR("WW");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("gpio driver for general");
